﻿using System;   
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

using Alicanto.Lexing;

namespace Alicanto.Compilation.Code.Blocks.Selection
{
    internal class If : ICodeGenerator
    {
        public Expression Code { get; private set; }

        public IEnumerable<ParseInstruction> Generate(TokenSequence tokens, Scope scope)
        {
            // > if
            ParseExpressionRemainder condition = new ParseExpressionRemainder();
            yield return condition;

            EnterScope enter = new EnterScope();
            yield return enter;

            Parse body = new Parse();
            yield return body;

            yield return new LeaveScope();

            LabelTarget iftrue = Expression.Label();
            LabelTarget iffalse = Expression.Label();

            List<Expression> block = new List<Expression>();
            block.Add(Expression.IfThenElse(
                condition.Remainder,
                Expression.Block(
                    Expression.Block(
                        body.Expressions
                    ),
                    Expression.Goto(iftrue)
                ),
                Expression.Goto(iffalse)
            ));

            // > else or elseif after this label
            block.Add(Expression.Label(iffalse));

            while (tokens.Peek(TokenType.Template) == Token.ElseIf || tokens.Peek(TokenType.Template) == Token.Else)
            {
                Token token = tokens.Skip(TokenType.Template).Pop();
                
                if (token == Token.Else)
                {
                    enter = new EnterScope();
                    yield return enter;

                    // > else
                    body = new Parse();
                    yield return body;

                    yield return new LeaveScope();

                    block.Add(Expression.Block(
                        body.Expressions
                    ));
                }
                else
                {
                    // > elseif
                    condition = new ParseExpressionRemainder();
                    yield return condition;

                    enter = new EnterScope();
                    yield return enter;

                    body = new Parse();
                    yield return body;

                    yield return new LeaveScope();

                    iffalse = Expression.Label();

                    block.Add(Expression.IfThenElse(
                        condition.Remainder,
                        Expression.Block(
                            Expression.Block(
                                body.Expressions
                            ),
                        Expression.Goto(iftrue)
                        ),
                        Expression.Goto(iffalse)
                    ));

                    // > else or elseif after this label
                    block.Add(Expression.Label(iffalse));
                }
            }

            // > outer statements after this label
            block.Add(Expression.Label(iftrue));

            this.Code = Expression.Block(
                block
            );
        }
    }
}
